package com.googlecode.nls4j.taglet;

import java.util.Locale;
import java.util.Map;

import com.googlecode.nls4j.NlsKey;
import com.sun.javadoc.Doc;
import com.sun.javadoc.FieldDoc;
import com.sun.javadoc.Tag;
import com.sun.tools.doclets.Taglet;

//@formatter:off
/**
 * This is a
 * <a href="http://download.oracle.com/javase/6/docs/technotes/guides/javadoc/taglet/overview.html">Javadoc Taglet</a>
 * which can be used to display default values of
 * {@linkplain Enum}-based {@linkplain NlsKey}s.<br/><br/>
 * <b>Syntax:</b><br/><br/>
 * <code>{&#064;defaultValue [locale]}</code><br/><br/>
 * <b>Usage example:</b>
 *
 * <pre>
 * public enum Msg implements NlsKey&lt;String&gt; {
 *     /** {&#64;defaultValue} &#42;/
 *     M1(&quot;M1 default value&quot;),
 *     /** {&#64;defaultValue en} &#42;/
 *     M2(&quot;M2 default value&quot;);
 * }
 * </pre>
 *
 * @author Andrey Pavlenko
 */
//@formatter:on
public class DefaultValueTaglet implements Taglet {

    /**
     * Adds new instance of this taglet to the specified {@code tagletMap}.
     */
    public static void register(final Map<String, Taglet> tagletMap) {
        final DefaultValueTaglet tag = new DefaultValueTaglet();
        tagletMap.put(tag.getName(), tag);
    }

    /**
     * Returns "defaultValue".
     */
    @Override
    public String getName() {
        return "defaultValue";
    }

    /**
     * Returns false.
     */
    @Override
    public boolean inConstructor() {
        return false;
    }

    /**
     * Returns true.
     */
    @Override
    public boolean inField() {
        return true;
    }

    /**
     * Returns false.
     */
    @Override
    public boolean inMethod() {
        return false;
    }

    /**
     * Returns false.
     */
    @Override
    public boolean inOverview() {
        return false;
    }

    /**
     * Returns false.
     */
    @Override
    public boolean inPackage() {
        return false;
    }

    /**
     * Returns false.
     */
    @Override
    public boolean inType() {
        return false;
    }

    /**
     * Returns true.
     */
    @Override
    public boolean isInlineTag() {
        return true;
    }

    /**
     * If the tag is of type {@linkplain FieldDoc} and the type of the field is
     * {@linkplain Enum} and {@linkplain NlsKey}, returns the
     * {@linkplain NlsKey#getDefaultValue(Locale) default value} of the
     * {@linkplain Enum} constant marked with this tag, otherwise - returns
     * tag.toString().
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Override
    public String toString(final Tag tag) {
        final Doc d = tag.holder();

        if (d instanceof FieldDoc) {
            final FieldDoc f = (FieldDoc) d;

            try {
                final Class c = Class.forName(f.type().qualifiedTypeName());

                if (Enum.class.isAssignableFrom(c)
                        && NlsKey.class.isAssignableFrom(c)) {
                    final NlsKey k = (NlsKey) Enum.valueOf((Class<Enum>) c,
                        f.name());
                    final String text = tag.text();
                    final Locale locale = (text != null) && (text.length() > 0) ? new Locale(
                            text) : Locale.getDefault();
                    return String.valueOf(k.getDefaultValue(locale));
                }
            } catch (final Exception ex) {
            }
        }

        return tag.toString();
    }

    /**
     * This method invokes the {@linkplain #toString(Tag)} method for each tag
     * from the specified array and concatenates the returned strings.
     */
    @Override
    public String toString(final Tag[] tags) {
        if (tags.length == 0) {
            return "";
        }

        final StringBuilder sb = new StringBuilder();

        for (final Tag t : tags) {
            sb.append(toString(t));
        }

        return sb.toString();
    }
}
